#ifndef __JSONOBJECT_H__
#define __JSONOBJECT_H__
#include <cstddef>
#include <limits>
#include <unordered_map>
#include <vector>
#include <map>
#include <vector>
#include <memory>
#include <fstream>
#include <functional>
#include <variant>
#include <type_traits>
#include <string_view>

using std::string_view_literals::operator""sv;


template <int size, class T, class Head, class... Tail>
constexpr int get_type_index_impl() {
    if constexpr (std::is_same_v<T, Head>) {
        return size - 1 - sizeof...(Tail);
    } else if constexpr (sizeof...(Tail) > 0) {
        return get_type_index_impl<size, T, Tail...>();
    } else {
        return -1;
    }
}

template <class T, class Vraiant>
struct get_type_index_t {
    static constexpr int value = -1;
};

template <class T, class... Ts, template<class...> class Tpl>
struct get_type_index_t<T, Tpl<Ts...>> {
    static constexpr int value = sizeof...(Ts) > 0 ? get_type_index_impl<static_cast<int>(sizeof...(Ts)), T, Ts...>() : -1;
};

template <class T, class Vraiant>
constexpr static auto get_type_index_v = get_type_index_t<T, Vraiant>::value;


class JsonObject;
using JsonObjectPtr = std::shared_ptr<JsonObject>;
using JsonDict = std::unordered_map<std::string, JsonObjectPtr>;
using JsonArray = std::vector<JsonObjectPtr>;

// 应该用variant代替union+index
using json_obj_t = std::variant<std::monostate, long long, double, bool, std::nullptr_t, std::string, JsonDict, JsonArray>;

enum ObjectType
{
    JSONINVALID = get_type_index_v<std::monostate, json_obj_t>,
    JSONINT = get_type_index_v<long long, json_obj_t>,
    JSONDOUBLE = get_type_index_v<double, json_obj_t>,
    JSONBOOL = get_type_index_v<bool, json_obj_t>,
    JSONNULLPTR = get_type_index_v<std::nullptr_t, json_obj_t>,
    JSONSTRING = get_type_index_v<std::string, json_obj_t>,
    JSONDICT = get_type_index_v<JsonDict, json_obj_t>,
    JSONARRAY = get_type_index_v<JsonArray, json_obj_t>
};

using std::string_view;
template <ObjectType T>
constexpr string_view object_name = "unknown";

template <>
constexpr string_view object_name<JSONINVALID> = "invalid";
template <>
constexpr string_view object_name<JSONINT> = "int";
template <>
constexpr string_view object_name<JSONDOUBLE> = "double";
template <>
constexpr string_view object_name<JSONBOOL> = "bool";
template <>
constexpr string_view object_name<JSONNULLPTR> = "null";
template <>
constexpr string_view object_name<JSONSTRING> = "string";
template <>
constexpr string_view object_name<JSONDICT> = "dict";
template <>
constexpr string_view object_name<JSONARRAY> = "array";

template <class Enum, class Variant, class Head, class... Tail>
constexpr bool check_variant_to_enum_impl() {
    return (noexcept(static_cast<Enum>(get_type_index_v<Variant, Tail>)) && ...);
}

template <class Enum, class Vraiant>
struct check_variant_to_enum_t {
    static constexpr bool value = false;
};

template <class Enum, class... Ts, template<class...> class Tpl>
struct check_variant_to_enum_t<Enum, Tpl<Ts...>> {
    static constexpr bool value = check_variant_to_enum_impl<Enum, Tpl<Ts...>, Ts...>();
};

template <class Enum, class Variant>
constexpr static auto check_variant_to_enum_v = check_variant_to_enum_t<Enum, Variant>::value;

static_assert(check_variant_to_enum_v<ObjectType, json_obj_t>, "ObjectType not contains all kind of json");

template <int Type>
struct jsontype {
    using type = typename std::conditional_t<(Type >= JSONINT || Type <= JSONINVALID), std::variant_alternative_t<Type, json_obj_t>, void>;
};

template <int Type>
using jsontype_t = typename jsontype<Type>::type;


template<class T>
inline JsonObjectPtr JSONOBJECT(const T& t)
{
    return std::make_shared<JsonObject>(t);
}

inline JsonObjectPtr JSONOBJECT()
{
    return std::make_shared<JsonObject>();
}

inline JsonObjectPtr JSONARRAYPTR(const std::vector<JsonObjectPtr>& v)
{
    return std::make_shared<JsonObject>(v);
}

inline JsonObjectPtr JSONDICTPTR(const std::unordered_map<std::string,JsonObjectPtr>& d)
{
    return std::make_shared<JsonObject>(d);
}

class JsonObject
{
public:
    explicit JsonObject();
    explicit JsonObject(void*);
    explicit JsonObject(std::nullptr_t);
    explicit JsonObject(int pi);
    explicit JsonObject(long long pl);
    explicit JsonObject(const std::string &ps);
    explicit JsonObject(const char *ps);
    explicit JsonObject(double pd);
    explicit JsonObject(bool pb);
    explicit JsonObject(const JsonDict &pt);
    explicit JsonObject(const JsonArray &pv);

    JsonObject(const JsonObject &jo) = default;
    JsonObject(JsonObject&& jo) = default;

    JsonObject &operator=(const JsonObject &jo) = default;
    JsonObject &operator=(JsonObject &&jo) = default;

    ~JsonObject() = default;

    static JsonObjectPtr decoder(const std::string &s);
    //采取左闭右开的方式
    static JsonObjectPtr findNextJsonObeject(const std::string &s, int &sta, int &end);
    static JsonObjectPtr findNextNumber(const std::string &s, int &sta, int &end);
    static JsonObjectPtr findNextString(const std::string &s, int &sta, int &end);
    static JsonObjectPtr parseDictStr(const std::string &s);
    static JsonObjectPtr findNextDict(const std::string &s, int &sta, int &end);
    static JsonObjectPtr findNextArray(const std::string &s, int &sta, int &end);
    static JsonObjectPtr findNextBool(const std::string &s, int &sta, int &end);
    static JsonObjectPtr findNextNullptr(const std::string &s, int &sta, int &end);
    static inline int firstNonEmpty(const std::string &s, int sta, int end, int step = 1, bool flag = true);
    static int skipEmptyOneChar(const std::string &s, int sta, int end, char c);
    static void reportError(const char *s, int x, int y = -1);
    static std::string replace(std::string s, std::string old_s, std::string new_s, int n = 1e9 + 7);
    ObjectType getType() const;
    string_view getTypeStr() const;
    // 将获得一份常引用，避免拷贝
    const long long &asInt() const;
    const double &asDouble() const;
    const std::string &asString() const;
    const JsonDict &asDict() const;
    const JsonArray &asArray() const;
    const bool &asBool() const; 
    // 将获得一份引用，来进行修改
    long long &asInt();
    double &asDouble();
    std::string &asString();
    JsonDict &asDict();
    JsonArray &asArray();
    bool &asBool();
    // 将获得一份拷贝
    long long getInt() const;
    double getDouble() const;
    std::string getString() const;
    JsonDict getDict() const;
    JsonArray getArray() const;
    bool getBool() const;
    std::nullptr_t getNullptr() const;
    std::nullptr_t asNullptr();
    static JsonObjectPtr copy(const JsonObject &jo);
    std::string convert(bool reverse = false) const;
    static std::string convert(std::string s, bool reverse = false);
    std::string json() const;
    // 当eps＞0时将int和double作为同类型进行比较,
    // 当order=true时要求JSONARRAY类型进行顺序比对，为false则要求乱序进行两次循环来验证JSONARRAY是否一致
    bool equal(const JsonObject &jo, double eps = -1, bool order = true) const;
    // 转为double类型的object,若不是数字类型则返回JSONINVALID
    JsonObject convertNumber() const;
    bool isNumber() const;

    template <class T, typename = std::enable_if<get_type_index_v<T, json_obj_t> != -1> >
    T conver() {
        return std::get<T>(this->json_obj);
    }

private:
    json_obj_t json_obj;
};


template <class T, typename = std::enable_if_t<get_type_index_v<std::remove_cv_t<T>, json_obj_t> != -1> >
int if_ex_set(T& set_val, const JsonObjectPtr& data, const std::string& key) {
    if (data->getType() == JSONDICT && data->asDict().find(key) != data->asDict().end()) { 
        set_val = data->asDict().at(key)->conver<T>();
        return 0;
    }
    return 1;
}

template <class T, typename = std::enable_if_t<get_type_index_v<std::remove_cv_t<T>, json_obj_t> == -1>, typename = std::enable_if_t<std::numeric_limits<T>::is_integer> >
int if_ex_set(T& set_val, const JsonObjectPtr& data, const std::string &key) {
    if (data->getType() == JSONDICT && data->asDict().find(key) != data->asDict().end()) { 
        set_val = static_cast<T>(data->asDict().at(key)->conver<long long>());
        return 0;
    }
    return 1;
}

#endif /* __JSONOBJECT_H__ */
